/**
 * 根据选择器查找满足条件的第一个元素
 * @param selector 选择器
 * @returns 满足选择器条件的第一个元素
 */
function $(selector) {
  return document.querySelector(selector)
}

/**
 * 生成从 min - max 范围内的随机整数
 * @param {*} min 下限
 * @param {*} max 上限
 * @returns 生成后的随机整数
 */
function random(min, max) {
  return Math.floor(Math.random() * (max - min)) + min
}

/**
 * 根据类名查找元素
 * @param {*} className 类名
 * @returns 根据类名查找到的元素数组（类数组对象）
 */
function getElementsByClassName(className) {
  // 支持，则直接使用
  if (document.getElementsByClassName) {
    return document.getElementsByClassName(className)
  }

  // 不支持，实现兼容写法
  var result = []
  var tags = document.getElementsByTagName('*')
  for (var i = 0, len = tags.length; i < len; i++) {
    // 当前遍历到元素使用的所有类名
    var classNames = tags[i].className.split(' ')
    // 遍历所有类名
    for (var j = 0, l = classNames.length; j < l; j++) {
      if (classNames[j] === className) {
        result.push(tags[i])
        break
      }
    }
  }
  return result
}

/**
 * 获取或设置 css 样式
 * @param element DOM 元素节点
 * @param name CSS 属性名称
 * @param value CSS 属性值
 */
function css(element, name, value) {
  // value 参数未传递
  if (typeof value === 'undefined') {
    // 根据属性名获取属性值
    if (typeof name === 'string') {
      // 根据 css 属性名获取对应属性值
      if (window.getComputedStyle) {
        return window.getComputedStyle(element, null)[name]
      }
      return element.currentStyle[name] // IE9 之前
    } else if (typeof name === 'object') {
      // 设置 css 样式
      for (var key in name) {
        element.style[key] = name[key]
      }
      return
    }
  }

  // 设置 css 样式
  element.style[name] = value
}

/**
 * 注册事件监听
 * @param {*} element 
 * @param {*} eventType 
 * @param {*} callback 
 */
function on(element, eventType, callback) {
  if (element.addEventListener) {
    element.addEventListener(eventType, callback, false)
  } else {
    eventType = 'on' + eventType
    element.attachEvent(eventType, callback)
  }
}

/**
 * 移除事件监听
 * @param {*} element 
 * @param {*} eventType 
 * @param {*} callback 
 */
function off(element, eventType, callback) {
  if (element.removeEventListener) {
    element.removeEventListener(eventType, callback, false)
  } else {
    eventType = 'on' + eventType
    element.detachEvent(eventType, callback)
  }
}

/**
 * 多属性运动函数
 * @param element 待添加运动效果的元素
 * @param options 对象，表示多属性运动时各属性及目标终值，如：{top: 300, left: 200, width: 200, opacity: 0.3}
 * @param duration 限定运动的总时长
 * @param fn 函数，可选，是运动结束后执行的函数
 */
function animate(element, options, duration, fn) {
  // 先清除元素上已有的定时运动动画效果
  clearTimeout(element.timer)
  // 初始值
  const start = {}
  // 单位时间运动距离
  const speed = {}
  // 遍历 options 中的所有属性，设置 start 、speed
  for (const key in options) {
    start[key] = parseFloat(css(element, key))
    speed[key] = (options[key] - start[key]) / duration
  }

  // 记录开始运动的起始时间
  const startTime = Date.now()

  // 运动，利用 setTimeout 启动定时器，重复实现运动效果
  const move = () => {
    // 实际运动时间，最后一步可能比限定总时间稍大，所以在实际时间与限定总时间之间取较小值
    const elapsed = Math.min(Date.now() - startTime, duration)
    // 各属性计算当前步位置
    for (const key in options) {
      const result = elapsed * speed[key] + start[key]
      element.style[key] = result + (key === 'opacity' ? '' : 'px')
    }

    // 判断是否继续启动定时器
    if (elapsed !== duration) {
      element.timer = setTimeout(move, 1000 / 60)
    } else {
      // 运动结束后，如果有要执行的任务，则调用函数执行
      fn && fn()
    }
  }
  // 调用函数，实现运动
  move()
}

/**
 * 淡入运动
 */
function fadeIn(element, duration, fn) {
  element.style.display = 'block'
  element.style.opacity = 0
  animate(element, {opacity: 1}, duration, fn)
}

/**
 * 淡出运动
 */
function fadeOut(element, duration, fn) {
  animate(element, {opacity: 0}, duration, () => {
    element.style.display = 'none'
    fn && fn()
  })
}


function $(selector) {
  return document.querySelector(selector)
}

/**
 * 利用 ajax 发送 post 请求
 * @param {*} url 资源地址
 * @param {*} data 数据
 */
function post(url, data) {
  return new Promise(function(resolve, reject) {
    // 创建核心对象
    const xhr = new XMLHttpRequest()
    // 建立连接
    xhr.open('POST', url, true)
    // 设置请求头
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    // 发送请求
    xhr.send(data)
    // 处理响应
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          const data = JSON.parse(xhr.responseText)
          resolve(data)
        } else {
          reject(new Error('访问异常：' + xhr.status))
        }
      }
    }
  })
}

/**
 * 保存 cookie
 * @param name cookie名称
 * @param value cookie值
 * @param options 可选选项：{expires: 7, path, domain, secure}
 */
function setCookie(name, value, options = {}) {
  // 处理失效时间
  if (options.expires) {
    const datetime = new Date()
    datetime.setDate(datetime.getDate() + options.expires)
    options.expires = datetime.toUTCString()
  }
  // 构建 cookie 字符串
  const cookie = [
    encodeURIComponent(name), // 将cookie名称编码
    '=',
    encodeURIComponent(value), // 将cookie值编码
    options.expires ? `; expires=${options.expires}` : '',
    options.path ? `; path=${options.path}` : '; path=/',
    options.domain ? `; domain=${options.domain}` : '',
    options.secure ? '; secure' : ''
  ].join('')
  // 保存
  document.cookie = cookie
}

// 删除 cookie
function removeCookie(name, options = {}) {
  options.expires = -1
  setCookie(name, '', options)
}

// 查询 cookie
function getCookie(name) {
  // 获取所有的 cookie
  const cookies = document.cookie.split('; ')
  // 遍历每条 cookie
  for (let i = 0, len = cookies.length; i < len; i++) {
    // 使用 = 号分割 key=value 结构
    const part = cookies[i].split('=')
    // 数组中第一个元素为 cookie 名
    // 剩余元素使用 = 号拼接后为 cookie 值
    const key = decodeURIComponent(part.shift())
    const value = decodeURIComponent(part.join('='))
    // 判断是否为查找的 cookie
    if (key === name) {
      return value
    }
  }

  // 未查找到
  return null
}

